package com.xiaoyu.ftp;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import com.xiaoyu.util2.FileUtil;
import com.xiaoyu.util2.PrintUtil;
import com.xiaoyu.util2.ValidUtil;

public class FTPUtilImpl implements FTPUtil {
	private FTPClient client;
	private FTPVo vo;

	public FTPUtilImpl(FTPVo vo) throws IOException {
		this.vo = vo;
		this.client = createFTPClien(vo);
	}

	private FTPClient createFTPClien(FTPVo vo) {
		FTPClient client = new FTPClient();
		int reply = -1;
		try {
			client.connect(vo.getHostName(), vo.getPort());
			client.login(vo.getUsername(), vo.getPassword());
			reply = client.getReplyCode();
			if (!(FTPReply.isPositiveCompletion(reply))) {
				client.disconnect();
				return null;
			}
			client.setControlEncoding(vo.getRemoteEncoding());
			client.setFileType(2);
			if (vo.isPassiveMode())
				client.enterLocalPassiveMode();
			else {
				client.enterRemotePassiveMode();
			}
			client.cwd(vo.getRemoteDir());
			return client;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public boolean reply(String operation) {
		int replyCode = this.client.getReplyCode();
		FTPLog log = new FTPLog();
		log.setHost(this.vo.getHostName());
		log.setOperation(operation);
		log.setLocalFile("");
		log.setRemoteFile("");
		log.setReplyCode(replyCode);
		log.setReplyCodeDesc((String) FTPConstant.REPLYCODE.get(Integer.valueOf(replyCode)));
		PrintUtil.println(log);
		return FTPReply.isPositiveCompletion(replyCode);
	}

	public boolean reply(String operation, String localFile, String remoteFile) {
		int replyCode = this.client.getReplyCode();
		FTPLog log = new FTPLog();
		log.setHost(this.vo.getHostName());
		log.setOperation(operation);
		log.setLocalFile(localFile);
		log.setRemoteFile(remoteFile);
		log.setReplyCode(replyCode);
		log.setReplyCodeDesc((String) FTPConstant.REPLYCODE.get(Integer.valueOf(replyCode)));
		PrintUtil.println(log);
		return FTPReply.isPositiveCompletion(replyCode);
	}

	public boolean isExists(String fileName) {
		List<String> list = listFile(this.vo.getRemoteDir());
		return (list.contains(fileName));
	}

	public boolean downLoad(String fileName) {
		String localfileName = this.vo.getLocalDir() + File.separator
				+ fileName;
		FileUtil.createFiles(localfileName);
		OutputStream out = null;
		try {
			out = new FileOutputStream(localfileName, true);
			this.client.retrieveFile(new String(fileName.getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1"), out);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return reply("DOWNLOAD", localfileName, fileName);
	}

	public boolean downLoadDir(String directory) {
		List<String> files = listFile(directory);
		for (String s : files) {
			downLoad(s);
		}
		return true;
	}

	public boolean deleteFile(String fileName) {
		if (isExists(fileName)) {
			try {
				this.client.deleteFile(new String(fileName.getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1"));
				return reply("DELETE", "", fileName);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return false;
	}

	public boolean deleteDir(String directory) {
		List<String> files = listFile(directory);
		try {
			for (String s : files) {
				deleteFile(s);
			}
			List<String> dirs = listDir(directory);
			for (int i = dirs.size() - 1; i >= 0; i--) {
				this.client.removeDirectory(new String(((String) dirs.get(i)).getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1"));
			}
			this.client.removeDirectory(new String(directory.getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return reply("DELETE", "", directory);
	}

	public boolean putFile(String fileName, String remoteFileName,
			boolean isDelete) {
		File file = new File(fileName);
		return putFile(file, remoteFileName, isDelete);
	}

	public boolean putFile(File file, String remoteFileName, boolean isDelete) {
		InputStream in = null;
		String fileName = remoteFileName;
		String path = "";
		String parent = getParentPath(remoteFileName);
		if (remoteFileName.lastIndexOf("/") != -1) {
			path = remoteFileName.substring(0, remoteFileName.lastIndexOf("/"));
			fileName = remoteFileName.substring(remoteFileName.lastIndexOf("/") + 1);
			mkDir(path);
			changeWorkDir(path);
		}
		try {
			in = new FileInputStream(file);
			if (isDelete) {
				deleteFile(new String(file.getName().getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1"));
			}
			this.client.appendFile(new String(fileName.getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1"), in);
			boolean bool = reply("UPLOAD", file.getAbsoluteFile().toString(), remoteFileName);
			return bool;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			changeWorkDir(parent);
			try {
				in.close();
			} catch (Exception e) {
			}
		}
		return false;
	}

	public boolean putDir(String fileName, String remoteDir) {
		File file = new File(fileName);
		return putDir(file, remoteDir);
	}

	public boolean putDir(File file, String remoteDir) {
		List<File> list = FileUtil.listFile(file);
		for (File f : list) {
			String name = f.getAbsolutePath();
			name = name.substring(name.indexOf(file.getName())).replaceAll(
					"\\\\", "/");
			putFile(f, remoteDir + "/" + name, true);
		}
		return true;
	}

	public List<String> listFile(String directory) {
		List<String> list = new ArrayList<String>();
		try {
			FTPFile[] files = this.client.listFiles(directory);
			for (int i = 0; i < files.length; i++) {
				String t = directory + "/" + files[i].getName().replaceAll("//", "/");
				if (files[i].isFile())
					list.add(t);
				else if (files[i].isDirectory())
					list.addAll(listFile(t + "/".replaceAll("//", "/")));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return list;
	}

	public Map<String, FileAttr> listFileAttr(String directory) {
		Map<String, FileAttr> map = new HashMap<String, FileAttr>();
		try {
			FTPFile[] files = this.client.listFiles(directory);
			for (int i = 0; i < files.length; i++)
				if (files[i].isFile()) {
					FTPFile file = files[i];
					String fileName = directory + file.getName();
					FileAttr attr = new FileAttr();
					attr.setFileName(fileName);
					attr.setModifyTime(file.getTimestamp().getTime());
					attr.setSize(Long.valueOf(file.getSize()));
					map.put(fileName, attr);
				} else if (files[i].isDirectory()) {
					map.putAll(listFileAttr(directory + files[i].getName() + "/"));
				}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return map;
	}

	public boolean changeWorkDir(String directory) {
		try {
			this.client.cwd(directory);
			return true;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	public String getWorkDir() {
		try {
			return this.client.printWorkingDirectory();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}

	public boolean mkDir(String directory) {
		directory = directory.replaceAll("//", "/");
		if (directory.startsWith("/")) {
			directory = directory.substring(1);
		}
		if (directory.endsWith("/"))
			directory = directory.substring(0, directory.length() - 1);
		try {
			String[] str = new String(directory.getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1").split("/");
			String t = "";
			String parnet = "";
			for (int i = 0; i < str.length; i++) {
				t = t + "/" + str[i];
				if (!(isExists(t.substring(1)))) {
					this.client.makeDirectory(str[i]);
				}
				this.client.changeWorkingDirectory(str[i]);
				parnet = parnet + "../";
			}
			if (str.length >= 1)
				this.client.changeWorkingDirectory(parnet);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}

	public boolean changName(String oldName, String newName) {
		return false;
	}

	public LinkedList<String> listDir(String directory) {
		LinkedList<String> list = new LinkedList<String>();
		try {
			FTPFile[] files = this.client.listFiles(directory);
			for (int i = 0; i < files.length; i++) {
				String t = directory + "/" + files[i].getName().replaceAll("//", "/");
				if (files[i].isDirectory()) {
					list.add(t);
					list.addAll(listDir(t + "/"));
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return list;
	}

	public FTPClient client() {
		return this.client;
	}

	public void destory() {
		if (!(ValidUtil.isValid(this.client)))
			return;
		try {
			this.client.disconnect();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unused")
	private String dealFileName(String file) {
		file.replaceAll("//", "/").replaceAll("\\\\", "/").replaceAll("\\\\\\\\", "/");
		try {
			return new String(file.getBytes(this.vo.getRemoteEncoding()), "ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}

	private String getParentPath(String file) {
		if (file.indexOf("/") != -1) {
			String temp = null;
			Pattern p = Pattern.compile("[/]+");
			Matcher m = p.matcher(file);
			int i = 0;
			while (m.find()) {
				temp = m.group(0);
				i += temp.length();
			}
			String parent = "";
			for (int j = 0; j < i; j++) {
				parent = parent + "../";
			}
			return parent;
		}
		return "./";
	}

	@SuppressWarnings("unused")
	private String getRelativePath(File path) {
		String path1 = path.getPath();
		String path2 = path.getAbsolutePath();
		return null;
	}
}